package maze.view;

import maze.app.Maze;
import maze.logic.LogicManager;

import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

public class Window
{
	/** game title */
	public static final String TITLE = "Emmy Maze";

	/** desired frame time */
	public static final int FRAMERATE = 60;

	/** window width */
	public static final int WIDTH = 1024;

	/** window height */
	public static final int HEIGHT = 768;

	/** color bits */
	public static final int BITS = 32;

	/** if finished, exit the game */
	public boolean finished;

	private long lastLoopTime = getTime();
	private long lastFpsTime = 0;
	private int fps;

	private long lastFire = 0;
	private long firingInterval = 100;

	private static Window instance = null;

	/**
	 * @return the display window instance
	 */
	public static Window getInstance()
	{
		if (instance == null)
			instance = new Window();
		return instance;
	}

	private Window()
	{
	}

	/**
	 * Initialise the game
	 */
	public void init() throws Exception
	{
		DisplayMode chosenMode = null;

		// list all the available display mode
		DisplayMode[] modes = Display.getAvailableDisplayModes();

		// select the display mode of targe width and height
		for (int i = 0; i < modes.length; i++)
		{
			if (modes[i].getWidth() == WIDTH && modes[i].getHeight() == HEIGHT && modes[i].getBitsPerPixel() == BITS)
			{
				chosenMode = modes[i];
				break;
			}
		}

		if (chosenMode == null)
		{
			throw new LWJGLException("the chosen display mode is not appropriate");
		}

		// create the display
		Display.setDisplayMode(chosenMode);
		Display.setFullscreen(false);
		Display.setTitle(TITLE);
		Display.create();

		// grab the mouse, dont want that hideous cursor when we're playing!
		// Mouse.setGrabbed(true);

		// enable textures since we're going to use these for our sprites
		GL11.glEnable(GL11.GL_TEXTURE_2D);
		GL11.glEnable(GL11.GL_BLEND);

		// disable the OpenGL depth test since we're rendering 2D graphics
		GL11.glDisable(GL11.GL_DEPTH_TEST);

		// put the window into orthographic projection mode with 1:1 pixel ratio.
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		// GL11.glOrtho(0.0, Display.getDisplayMode().getWidth(), 0.0,
		// Display.getDisplayMode().getHeight(), -1.0, 1.0);
		float ratio = (float) Display.getDisplayMode().getWidth() / (float) Display.getDisplayMode().getHeight();
		GL11.glOrtho(0.0, ratio * Maze.TOTAL_UNIT, 0.0, Maze.TOTAL_UNIT, -1.0, 1.0);
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glLoadIdentity();
		GL11.glViewport(0, 0, Display.getDisplayMode().getWidth(), Display.getDisplayMode().getHeight());
	}

	/**
	 * Runs the game
	 */
	public void run()
	{
		while (!finished)
		{
			// calculate fps
			calculateFPS();

			// always call Window.update(), all the time - it does some behind the
			// scenes work, and also displays the rendered output
			Display.update();

			if (Display.isCloseRequested())
			{
				// check for close requests
				finished = true;
			}
			else if (Display.isActive())
			{
				// the window is in the foreground, so we should play the game
				logic();
				render();
				Display.sync(FRAMERATE);
			}
			else
			{
				// the window is not in the foreground, so we can allow other stuff to run and
				// infrequently update
				try
				{
					Thread.sleep(100);
				}
				catch (InterruptedException e)
				{
				}

				logic();

				// only bother rendering if the window is visible or dirty
				if (Display.isVisible() || Display.isDirty())
				{
					render();
				}
			}
		}
	}

	/**
	 * do any game-specific cleanup
	 */
	public void cleanup()
	{
		// close the window
		Display.destroy();
	}

	/**
	 * Render the current frame
	 */
	private void render()
	{
		SpriteManager.getInstance().render();
	}

	/**
	 * do all calculations, handle input, etc.
	 */
	private void logic()
	{
		LogicManager.getInstance().logic();
	}

	/**
	 * calculate the FPS
	 */
	private void calculateFPS()
	{
		long delta = getTime() - lastLoopTime;
		lastLoopTime = getTime();
		lastFpsTime += delta;
		fps++;

		// update our FPS counter if a second has passed
		if (lastFpsTime >= 1000)
		{
			Display.setTitle(TITLE + " (FPS: " + fps + ")");
			lastFpsTime = 0;
			fps = 0;
		}
	}

	public boolean tryToFire()
	{
		// check if waited long enough to fire
		if (System.currentTimeMillis() - lastFire < firingInterval)
		{
			return false;
		}

		// waited long enough
		lastFire = System.currentTimeMillis();

		return true;
	}

	public static long getTime()
	{
		// we get the "timer ticks" from the high resolution timer
		// multiply by 1000 so our end result is in milliseconds
		// then divide by the number of ticks in a second giving
		// us a nice clear time in milliseconds
		return (Sys.getTime() * 1000) / Sys.getTimerResolution();
	}
}
